﻿namespace CookieTerm
{
    using System.IO;
    using System.Text;

    public abstract class AnsiCommand
    {
        protected int[] defaults = new int[0];
        protected int[] parameters;
        protected int parameterLength;

        public int[] Parameters
        {
            get
            {
                return this.parameters;
            }
        }

        public virtual void LoadParameters(int?[] param, int length)
        {
            if (this.parameterLength < length)
            {
                throw new InvalidDataException("Too many parameters");
            }

            this.parameters = new int[this.parameterLength];
            for (int i = 0; i < this.parameterLength; i++)
            {
                if (i >= length || !param[i].HasValue)
                {
                    if (i >= this.defaults.Length)
                    {
                        throw new InvalidDataException("Parameter cannot be ommitted");
                    }

                    this.parameters[i] = this.defaults[i];
                }
                else
                {
                    this.parameters[i] = param[i].Value;
                }
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(this.GetType().Name);
            foreach (int p in this.parameters)
            {
                sb.Append(" ");
                sb.Append(p);
            }

            return sb.ToString();
        }
    }

    public abstract class CursorMovement : AnsiCommand
    {
        public CursorMovement()
        {
            this.parameterLength = 1;
            this.defaults = new int[] { 1 };
        }
    }

    public class CursorUp : CursorMovement
    {
    }

    public class CursorDown : CursorMovement
    {
    }

    public class CursorForward : CursorMovement
    {
    }

    public class CursorBack : CursorMovement
    {
    }

    public class CursorNextLine : CursorMovement
    {
    }

    public class CursorPrevLine : CursorMovement
    {
    }

    public class CursorHorizontal : CursorMovement
    {
        public CursorHorizontal()
        {
            // Override because the parameter cannot be ommitted like other cursor movement commmands.
            this.parameterLength = 1;
        }
    }

    public class CursorPosition : AnsiCommand
    {
        public CursorPosition()
        {
            this.parameterLength = 2;
            this.defaults = new int[] { 1, 1 };
        }
    }

    public class EraseInDisplay : AnsiCommand
    {
        public EraseInDisplay()
        {
            this.parameterLength = 1;
            this.defaults = new int[] { 0 };
        }

        public override void LoadParameters(int?[] parameters, int parametersLength)
        {
            base.LoadParameters(parameters, parametersLength);
            if (this.parameters[0] > 2 || this.parameters[0] < 0)
            {
                throw new InvalidDataException("Invalid parameter for ANSI sequence ED");
            }
        }
    }

    public class EraseInLine : AnsiCommand
    {
        public EraseInLine()
        {
            this.parameterLength = 1;
            this.defaults = new int[] { 0 };
        }

        public override void LoadParameters(int?[] parameters, int parametersLength)
        {
            base.LoadParameters(parameters, parametersLength);
            if (this.parameters[0] > 2 || this.parameters[0] < 0)
            {
                throw new InvalidDataException(string.Format("Invalid parameter {0} for ANSI sequence EraceInLine", this.parameters[0]));
            }
        }
    }

    public class SelectGraphicRendition : AnsiCommand
    {
        public override void LoadParameters(int?[] parameters, int parameterLength)
        {
            this.parameterLength = parameterLength;
            this.defaults = new int[this.parameterLength];
            base.LoadParameters(parameters, parameterLength);
        }
    }

    public class InsertLine : AnsiCommand
    {
        public InsertLine()
        {
            this.parameterLength = 1;
            this.defaults = new int[] { 1 };
        }
    }

    public class DeviceStatusReport : AnsiCommand
    {
        public override void LoadParameters(int?[] parameters, int parameterLength)
        {
            if (parameterLength != 1 || parameters[0] != 6)
            {
                throw new InvalidDataException("DSC should have format 'ESC [ 6 n'");
            }
        }
    }

    public class SaveCursorPosition : AnsiCommand
    {
        public SaveCursorPosition()
        {
            this.parameterLength = 0;
        }
    }

    public class RestoreCursorPosition : AnsiCommand
    {
        public RestoreCursorPosition()
        {
            this.parameterLength = 0;
        }
    }
}
